%{ 
open Ast;;
let labelNum = ref 0;;	(* Placeholder for the numbering of the labels *)

let createLabels () = labelNum := (!labelNum + 1); Labels(Pre(!labelNum), Post(!labelNum));;

let createDecl var = Declaration(createLabels (), var);;
let createDeclComp decl1 decl2 = Declcomp(createLabels (), decl1, decl2);;
%}

%token <string> IDENTIFIER
%token <char> BADSEQUENCE
%token T F UNDET NOT EQUAL AND OR EOF LPAREN RPAREN NOTEQUAL IMPLICATION XOR
%token SKIP COMPOUND IF THEN ELSE ENDIF ELSEIF ASSIGNMENT LISTSEPARATOR WHILE BEGINWHILE ENDWHILE RETURN 
%token DECLARATION
%token VOID BOOL BEGINPROC ENDPROC

%start bp
%type <Ast.bp> bp

%left COMPOUND
%left IF
%left IMPLICATION
%left AND OR XOR
%left EQUAL NOTEQUAL
%left NOT
%left LPAREN RPAREN
%left T F

%%

bp:
	declaration procedurelist EOF { Bp($1, $2) }
	| procedurelist EOF { Bp(Empty, $1) }
	;

expression:
	T             { Value(True) }
	| F             { Value(False) }
	| IDENTIFIER	{ Variable $1 }
	| NOT expression { Not $2 }
	| expression EQUAL expression  { Equal($1, $3) }
	| expression NOTEQUAL expression  { Not(Group(Equal($1, $3))) }
	| expression XOR expression  { Not(Group(Equal($1, $3))) }
	| expression AND expression { And($1, $3) }
	| expression OR expression { Or($1, $3) }
	| LPAREN expression RPAREN { Group($2) }
	| expression IMPLICATION expression { Or(Not $1, $3) }
	;
  
decider:
  expression { Expression $1 }
  | UNDET { Undet }
  ;;

stm:
	SKIP	{ Skip(createLabels ()) }
	| stm COMPOUND stm	{ Comp(createLabels (), $1, $3) }
	| stm COMPOUND 	{ $1 }
	| ifstm	{ $1 }
	| IDENTIFIER ASSIGNMENT expression	{ Assignment(createLabels (), [$1], [$3]) }
	| idlist ASSIGNMENT expressionlist	{ Assignment(createLabels (), $1, $3) }
	| IDENTIFIER LPAREN expressionlist RPAREN 	{ Callvoid(createLabels (), $1, $3) } // void call
	| IDENTIFIER LPAREN RPAREN 	{ Callvoid(createLabels (), $1, []) } // When the procedure takes no formal parameters! 
	| IDENTIFIER ASSIGNMENT IDENTIFIER LPAREN expressionlist RPAREN 	{ Callbool(createLabels (), $1, $3, $5) } //bool call
	| IDENTIFIER ASSIGNMENT IDENTIFIER LPAREN RPAREN 	{ Callbool(createLabels (), $1, $3, []) } // When the procedure takes no formal parameters! 
	| WHILE LPAREN decider RPAREN BEGINWHILE stm ENDWHILE	{ While(createLabels (), $3, $6) }
	| RETURN	{ Returnvoid(createLabels ()) }
	| RETURN expression 	{ Returnbool(createLabels (), $2) }
	;

idlist:
	IDENTIFIER 	{ $1 :: [] }
	| IDENTIFIER LISTSEPARATOR idlist	{ $1 :: $3 }
	;

expressionlist:
	expression 	{ $1 :: [] }
	| expression LISTSEPARATOR expressionlist	{ $1 :: $3 }
	;

ifstm:
	IF LPAREN decider RPAREN THEN stm ENDIF { Ifelse(createLabels (), $3, $6, Skip(createLabels ())) }
	| IF LPAREN decider RPAREN THEN stm ELSE stm ENDIF	{ Ifelse(createLabels (), $3, $6, $8) }
	| IF LPAREN decider RPAREN THEN stm elseif ENDIF { Ifelse(createLabels (), $3, $6, $7) }
	;

elseif:
    ELSEIF LPAREN decider RPAREN THEN stm elseif 	{ Ifelse(createLabels (), $3, $6, $7) }
	| ELSEIF LPAREN decider RPAREN THEN stm ELSE stm	{ Ifelse(createLabels (), $3, $6, $8) }
  | ELSEIF LPAREN decider RPAREN THEN stm	{ Ifelse(createLabels (), $3, $6, Skip(createLabels ())) }
	;
	
declaration:
	DECLARATION idlist COMPOUND declaration 	{ 	if List.length $2 = 1 then
														Declcomp(createLabels (), createDecl (List.hd $2), $4)
													else
														let Declcomp(labels, d1, d2) = (* createDeclComp always returns a declComp *)
                            let decll = List.map createDecl $2 in 
                              List.fold_left createDeclComp (List.hd decll) (List.tl decll) in
															Declcomp(labels, d2, Declcomp(createLabels(), $4, d1))
												}
	| DECLARATION idlist COMPOUND 	{ 	if List.length $2 = 1 then
											createDecl (List.hd $2)
										else
											let Declcomp(labels, d1, d2) = let decll = List.map createDecl $2 in List.fold_left createDeclComp (List.hd decll) (List.tl decll) in
												Declcomp(labels, d2, d1)
									}
	;

proctype:
	BOOL { Bool }
	| VOID { Void }
	;
	
procedure:
	proctype IDENTIFIER LPAREN idlist RPAREN BEGINPROC declaration stm ENDPROC 	{ Procedure(createLabels (), $1, $2, $4, $7, $8) }
	| proctype IDENTIFIER LPAREN RPAREN BEGINPROC declaration stm ENDPROC 	{ Procedure(createLabels (), $1, $2, [], $6, $7) } // no formal parameters
	| proctype IDENTIFIER LPAREN idlist RPAREN BEGINPROC stm ENDPROC 	{ Procedure(createLabels (), $1, $2, $4, Empty, $7) } // no declarations
	| proctype IDENTIFIER LPAREN RPAREN BEGINPROC stm ENDPROC 	{ Procedure(createLabels (), $1, $2, [], Empty, $6) } // no formal parameters or no declarations
	;
	
procedurelist: 
	procedure	{ $1 :: [] }
	| procedure procedurelist { $1 :: $2 }
	;